package de.justphil.tcg.tcgserver.rest.db.mapstores;

import static de.justphil.tcg.tcgserver.commons.logging.TCGLogging.restLogger;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.TypedQuery;

import org.slf4j.Logger;

import com.hazelcast.core.MapStore;

import de.justphil.tcg.tcgserver.commons.domain.CardInstance;
import de.justphil.tcg.tcgserver.commons.domain.Game;
import de.justphil.tcg.tcgserver.commons.domain.User;
import de.justphil.tcg.tcgserver.commons.management.Application;
import de.justphil.tcg.tcgserver.rest.db.mapstores.helpers.LoadHelper;

public class UserMapStore implements MapStore<Long, User> {

	private static final Logger log = restLogger(UserMapStore.class);
	
	private final EntityManagerFactory factory;

	public UserMapStore() {
		log.debug("New object created!");
		factory = Application.getEntityManagerFactory();
	}
	
	@Override
	public User load(Long userId) {
		log.debug("load()");
		
		EntityManager em = factory.createEntityManager();
		
		User user = em.find(User.class, userId);
		loadTransientFields(em, user);
		
		em.close();
		
		return user;
	}

	@Override
	public Map<Long, User> loadAll(Collection<Long> userIds) {
		log.debug("loadAll()");
		
		EntityManager em = factory.createEntityManager();
		Map<Long, User> userMap = new HashMap<Long, User>();
		
		List<User> userList = getUserList(em);
		for(User user : userList) {
			loadTransientFields(em, user);
			userMap.put(user.getId(), user);
		}
		
		em.close();
		
		return userMap;
	}

	@Override
	public Set<Long> loadAllKeys() {
		log.debug("loadAllKeys()");
		
		EntityManager em = factory.createEntityManager();
		
		Set<Long> userIds = new HashSet<Long>();
		
		List<User> userList = getUserList(em);
		for (User user : userList) {
			userIds.add(user.getId());
		}
		
		em.close();
		
		return userIds;
	}
	
	private void loadTransientFields(EntityManager em, User user) {
		if (user == null) {
			return;
		}
		
		// Load decks for every card instance
		for (CardInstance ci : user.getCardInstances()) {
			LoadHelper.loadIncludingDecks(em, ci);
		}
		
		// Load players for every played game
		for (Game game : user.getPlayedGames()) {
			LoadHelper.loadGamePlayers(em, game);
		}
	}
	
	private List<User> getUserList(EntityManager em) {
		TypedQuery<User> q = em.createQuery("SELECT u FROM User u ORDER BY u.id", User.class);
		return q.getResultList();
	}

	@Override
	public void delete(Long userId) {
		log.debug("delete()");
		
		EntityManager em = factory.createEntityManager();
		em.getTransaction().begin();
		
		User user = em.find(User.class, userId);
		if (user != null) {
			em.remove(user);
		}
		
		em.getTransaction().commit();
		em.close();
	}

	@Override
	public void deleteAll(Collection<Long> userIds) {
		log.debug("deleteAll()");
		
		EntityManager em = factory.createEntityManager();
		em.getTransaction().begin();
		
		User user = null;
		for (Long userId : userIds) {
			user = em.find(User.class, userId);
			if (user != null) {
				em.remove(user);
			}
		}
		
		em.getTransaction().commit();
		em.close();
	}

	@Override
	public void store(Long userId, User user) {
		log.debug("store()");
		
		EntityManager em = factory.createEntityManager();
		em.getTransaction().begin();
		
		User pUser = em.find(User.class, userId);
		if (pUser == null) {
			log.debug("em.persist()");
			em.persist(user);
		}
		else {
			log.debug("em.merge()");
			em.merge(user);
		}
		
		em.getTransaction().commit();
		em.close();
	}

	@Override
	public void storeAll(Map<Long, User> userMap) {
		log.debug("storeAll()");
		
		EntityManager em = factory.createEntityManager();
		em.getTransaction().begin();
		
		User pUser	= null;
		User user	= null;
		for (Long userId : userMap.keySet()) {
			user = userMap.get(userId);
			
			pUser = em.find(User.class, userId);
			if (pUser == null) {
				log.debug("em.persist()");
				em.persist(user);
			}
			else {
				log.debug("em.merge()");
				em.merge(user);
			}
		}
		
		em.getTransaction().commit();
		em.close();
	}

}
